package com.google.code.expr.lang;

import java.io.IOException;

/**
 * Thrown during parsing when there is a parse exception.
 * @author critchie
 *
 */
@SuppressWarnings("serial")
public class EXPRParseException extends IOException {

	public EXPRParseException(int pLineNumber, int pColumn, ParseException e) {
		super(e.getMessage(),e);
	}
	public EXPRParseException(int pLineNumber, int pColumn, TokenMgrError e) {
		super(e.getMessage(),e);
	}
	public EXPRParseException(int pLineNumber, int pColumn, String pMessage, Throwable pCause) {
		super(pMessage+" [line "+pLineNumber+", column"+pColumn+"]",pCause);
	}
	
	public EXPRParseException(int pLineNumber, int pColumn, String pMessage) {
		this(pLineNumber,pColumn,pMessage,null);
	}
	
	public EXPRParseException(EXPRAtom pAtom, String pMessage, Throwable pCause) {
		this(pAtom.getLineNumber(),pAtom.getColumn(),pMessage,pCause);
	}
	
	public EXPRParseException(EXPRAtom pAtom, String pMessage) {
		this(pAtom.getLineNumber(),pAtom.getColumn(),pMessage,null);
	}
	
	public EXPRParseException(ParseException e,EXPRCommandParser pParser) {
		super(adjustParseExceptionMessage(e.getMessage(),pParser),e);
	}
	
	public EXPRParseException(TokenMgrError e,EXPRCommandParser pParser) {
		super(adjustTokenMgrErrorMessage(e.getMessage(),pParser),e);
	}
	
	/**
	 * Fixes the message's line number and format if the message contains:
	 * 
	 * at line X, column X.
	 * 
	 * @param pMessge
	 * @param pParser
	 * @return
	 */
	private static String adjustParseExceptionMessage(String pMessge, EXPRCommandParser pParser) {
		return adjust(pMessge,"at line ",", ", ", column ",".",pParser);
	}
	
	/**
	 * Fixes the message's line number and format if the message contains:
	 * 
	 * at line X, column X.
	 * 
	 * @param pMessge
	 * @param pParser
	 * @return
	 */
	private static String adjustTokenMgrErrorMessage(String pMessge, EXPRCommandParser pParser) {
		return adjust(pMessge,"at line ",", ", ", column ",".",pParser);
	}
	/**
	 * This is a huge hack.  The line numbers and columns are wrong in the jcc compiler, because it
	 * only has a small section within the file.  the parser has the offset, but the generated 
	 * exception classes can't be changed.  So this method looks for the exception messages
	 * and replaces the line number and column.  It only works if the message is constructed within
	 * the expected format.
	 * 
	 * @param pMessage
	 * @param pLinePrefix
	 * @param pLineSuffix
	 * @param pColumPrefix
	 * @param pColumnSuffix
	 * @param pParser
	 * @return
	 */
	private static String adjust(String pMessage, String pLinePrefix, String pLineSuffix, String pColumPrefix, String pColumnSuffix, EXPRCommandParser pParser) {
		int lineStart = pMessage.indexOf(pLinePrefix);
		if (lineStart>=0) {
			lineStart +=  pLinePrefix.length();
			int lineEnd = pMessage.indexOf(pLineSuffix,lineStart);
			if (lineEnd>=0) {
				try {
					int oldLineNumber = Integer.parseInt(pMessage.substring(lineStart,lineEnd));
					int columnStart = pMessage.indexOf(pColumPrefix,lineEnd);
					if (columnStart>=0) {
						columnStart +=  pColumPrefix.length();
						int columnEnd = pMessage.indexOf(pColumnSuffix,columnStart);
						if (columnEnd>=0) {
							int oldColumn = Integer.parseInt(pMessage.substring(columnStart,columnEnd));
						
							return pMessage.substring(0,lineStart) + pParser.calculateLineNumber(oldLineNumber, oldColumn) 
								+ pMessage.substring(lineEnd,columnStart) + pParser.calculateColumn(oldLineNumber, oldColumn) 
								+ pMessage.substring(columnEnd);
						}
					}
				}
				catch (Throwable e) {
					e.printStackTrace();
					//do nothing.  leave the message as it was.
				}
			}
		}
		return pMessage;
	}
}
